Le 20 avril 2003. Une tentative de normalisation du code source de nos scripts PHP Une lecture du document http://alltasks.net/code/php_coding_standard.html est conseillée, bien que la norme definie ici soit en plusieurs points differente. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ * Nomination des Objets : > Variables et fonctions > Constantes > Fichiers bibliotheques de fonctions > Fichiers de configuration * Presentation Globale : > Extension de fichier > Format de fichier > Lignes > Commentaires > Indentation > Accolades > Switch > Fonctions > Balises PHP > Cas Particulier * Presentation Locale : > Parenthesage > Concatenation > Affectation > Operateurs unaires > Operateurs binaires/ternaires * Recommandations : > Tests booleens > Utilisation des tableaux (array) > Affectations imbriquees > $_POST et $_GET > "Gotcha Keywords" > if (FALSE) { } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ * Nomination des Objets : L'utilisation du franglais est autorisee, il est conseille de n'utiliser qu'une seule langue. > Variables et fonctions doivent avoir des noms les plus explicites ou mnémoniques. Les abréviations sont TOLEREES dans la mesure ou elles permettent de réduire significativement la taille du nom sans en perdre le sens. - pour une variable certains suffixes sont tres utiles Max - valeur maximale Cnt - variable de comptage (hors boucle for) Key - valeur 'cle' - pour une fonction certains suffixes sont a retenir is - pour une question dont la reponse est un booleen (demande de confirmation) can - pour une question dont la reponse est un booleen (demande de permission) get - pour recuperer une valeur set - pour fixer une valeur exemple: $result= @mysql_query("SELECT count(msg_id) FROM messages WHERE ....", $link); $msgCnt= $result ? @mysql_result($result,0,0) : 0 ; Function isMsgAuthor() Function getLevel() Function canDeleteMsg() Dans le cas de noms composes, la premiere lettre de chaque mot suivant le premier est en majuscule (conseille mais pas obligatoire). ex: getMailHeader() Les noms de variables commencent par une minuscule. Les noms de fonctions sont prefixes par 'SK_' (ex: SK_getMailHeader() ) (regle OBLIGATOIRE pour l'ecriture de bibliotheque de fonctions) > Les noms de constantes s'ecrivent exclusivement en MAJUSCULES, dans le cas de noms composes, chaque mot est separe par un trait bas '_' > Les Noms de fichiers bibliotheques de fonctions s'ecrivent en minuscule et sont OBLIGATOIREMENT prefixes par 'SKlib_' il est FORTEMENT conseillé d'utiliser un nom simple (en un mot) ex: SKlib_string.php , SKlib_session.php Lorsque qu'un fichier de definitions de constantes est lie a celui d'une bibliotheque, on reprendra le meme nom que le fichier bibliotheque, en remplacant le prefixe 'SKlib_' par 'SKcst_' ex: SKcst_session.php > Les Noms de fichiers de configuration s'ecrivent en minuscules. On choisira un nom simple et evocateur. ex: sitemap.php /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ * Presentation Globale : > Tous les fichiers PHP auront l'extension .php (PAS DE .inc, .php3, etc..) > Les fichiers sources sont OBLIGATOIREMENT au format UNIX (les retours a la ligne se font par un simple 'LF' -Line Feed-) > Autant que possible, on evitera les lignes de plus de 80 caracteres. Il arrive que des requetes SQL depassent cette limite, mais la clarte de la requete est la plus importante. > Un commentaire se place avant l'instruction, ou le groupe d'instructions, expliquee, et sera en retrait d'un caractere par rapport au debut de ligne (voir indentation plus bas). Les commentaires seront a rediger en anglais. Suivant le type de fichier, les styles de commentaires seront different; Dans le cas d'un fichier de config/definition de constantes: # commentaire Sinon: // commentaire ou /* commentaire */ Les commentaires sur plusieurs lignes respecteront l'alignement suivant: /* * ligne 1 * ligne 2 */ On veillera a simplifier la lecture en separant les blocs 'logiques' par des lignes horizontales : /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ Les fichiers librairies et fichiers de definition de constantes debuteront par un commentaire expliquant le fonctionnement global du code. On donnera aussi, et c'est important, la liste des fonctions definies dans le fichier, DANS L'ORDRE DE DEFINITION (comme un index) > L'indentation est realise au moyen d'espaces, la taille d'une identation est egale a DEUX caracteres. Les tabulations font HUIT caracteres. Il est possible de remplacer 8 espaces par une tabulation. Les commentaires sont en retrait de UN caractere a gauche. > La disposition des accolades est du style "Allmann", le style "K&R" est PROSCRIT. On passe a la ligne apres une structure de controle ou une accolade fermante '}', il est autorise de continuer la ligne apres une accolade ouvrante '{'. Il est conseille de toujours utiliser les accolades, meme pour une unique instruction. ex: if ($mode == SK_ENCRYPT) { for ($i=1 ; $i<($dlength+8) ; $i++) { $result[$i]= chr( ord($result[$i]) ^ ord($result[$i-1]) ); } } else { for ($i=$dlength ; $i-->8 ; ) { $result[$i]= chr( ord($result[$i]) ^ ord($result[$i-1]) ); } } Dans un soucis de clarte, Lorsqu'un bloc de code fait plus de 4-5 lignes, il est recommande de placer un commentaire reprenant la structure de controle apres l'accolade fermante. > L'utilisation de 'switch' est encouragee é_è On passe a la ligne apres ':' et on indente. S'il n'y a qu'une instruction suivant un branchement, le retour a la ligne n'est pas obligatoire. Pour essayera de bien separer les cas en laissant des lignes vides, en particulier apres un 'break', mais aussi avant le cas par defaut. ex: switch( getUserInfosClear($login,$passw,$struct) ) { case 0 : // $struct[3] donne le level, $struct[2] l'uid if ( !isValidLevel($struct[3]) ) { return (ERR_UNKNOW_LEVEL); } else if ( !isValidUsrId($struct[2]) ) { return (ERR_UNKNOW_ACCOUNT); } else if ( isCPNOLOGIN($struct[3]) ) { return (ERR_CPNOLOGIN); } // Il manque un else ici break; case 3 : return (ERR_UNKNOW_ACCOUNT); case 4 : return (ERR_WRONG_PASSW); case 5 : return (ERR_ACCES_DENIED); default : return (ERR_INCOMPLETE_FORM); }// switch( getUserInfos ) > Les declarations de fonctions devront etre precede par un commentaire explicatif. On utilisera une presentation "a la" javadoc: On utilisera deux '*' a l'ouverture du commentaire, et on passera a la ligne. Le commentaire debutera par au minimum une, si necessaire plusieurs, phrase, terminée par un point '.', decrivant la fonction. Puis une presentation de chaque argument (si argument il y a), et enfin une description de la valeur de retour (si retour il y a). L'utilisation des mots cles suivants est recommandee. Se referrer a la documentation javadoc pour de plus amples explications. > http://java.sun.com/j2se/javadoc/writingdoccomments/ @author @param @return @see @since @version Dans le cas ou l'utilisation d'une variable est necessaire pour stocker la valeur de retour d'un fonction, cette derniere sera OBLIGATOIREMENT nommée '$result'. ex: /** * Build up a proper key, without repetition patterns, from the $key * parameter. * The length of the new key is greater or equal than the data length. * * @param $key original key * @param $dlength data length * @return new key */ function SK_buildKey($key, $dlength) { $result= md5($key); if (KEY_STRENGHT == 0) { while (strlen($result) < $dlength) { $result.= md5($result); } } else { $strength= KEY_STRENGHT/8; while ( strlen($result) <= $strength ) { $result.= md5($result); } $base= substr($result, 0, $strength); while ( strlen($result) < $dlength ) { $result.= $base; } } return( $result ); } On utilisera le nom '$answer' pour les resultats de requetes SQL ex : /** * Retrieves the name of the select database for the current connection * (represented by the link parameter). * 'SK_MySql_getCurrentDb()' returns an empty string on failure or if the * supplied link identifier is found to be invalid. * * :INFO: MySQL 'DATABASE()' function * DATABASE() Returns the current database name, if there is no current * database, DATABASE() returns the empty string. * * mysql> SELECT DATABASE(); * -> 'test' * * @param $link MySQL link identifier * @return Database name for the current connection */ function SK_MySql_getCurrentDb($link=0) { if (0 != $link) { $answer= @mysql_query('SELECT DATABASE()', $link); if (0 != $answer) { $result= @mysql_result($answer, 0, 0); @mysql_free_result($answer); return ($result); }// if (0 != $answer) }// if (0 != $link) return (''); } > Il est FORTEMENT RECOMMANDE d'utiliser la balise d'ouverture PHP complete "" est autorise, mais fortement deconseille. On passe a la ligne apres la balise d'ouverture, et avant la balise de fermeture. La balise d'ouverture PHP DOIT se trouver a la premiere colonne, le code commence a la troisieme colonne. Ex: > CAS PARTICULIER : Lorsque le code PHP est insere au coeur meme d'un fichier HTML, il est alors recommande d'etre le plus CONCIS possible. A noter: on evitera les fonctions echo/printf pour afficher du HTML. Le code PHP commence a une tabulation du bord. ex:
dummy text dummy text dummy text dummy text dummy text dummy text
dummy text dummy text dummy text

dummy text dummy text

On pourra aussi, DANS CE CAS uniquement, utiliser la syntaxe alternative. > http://fr.php.net/manual/fr/control-structures.alternative-syntax.php
dummy text dummy text dummy text dummy text dummy text dummy text
dummy text dummy text dummy text

dummy text dummy text

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ * Presentation Locale : > Pas d'espace entre le nom d'une fonction et la parenthese ouvrante '(' Au contraire, il est bienvenu (mais pas exige) de placer un espace entre un mot cle (while, switch, for, ...) et sa parenthese ouvrante. Pour la declaration d'une boucle 'for', on separera chaque membre en inserant un espace de part et d'autre des points virgules. ex: // reduction du nombre d'argument a un multiple de deux if ( $numArg= $numArg - ($numArg%2) ) { for ($i=1 ; $i<$numArg ; $i+=2) { $result.= func_get_arg($i) .'='. func_get_arg($i+1) .'&'; } } Lorsque 'return' prend un argument, ce dernier doit etre entre parentheses. ex: return ($result); > L'operateur de concatenation de chaine sera toujours accole a une chaine literale, et toujours separe par un espace des variables et autres appels de fonctions. ex: if ( isset($parsed['query']) ) { $result.= '?'. $parsed['query'] .'&'. $sessId; } else { $result.= '?'. $sessId; } if ( isset($parsed['fragment']) ) { $result.= '#'. $parsed['fragment']; } return ($result); > Concernant l'affectation, on evitera d'inserer un espace entre la L-value et l'operateur, mais on en placera volontier entre l'operateur et la valeur. Cette regle n'est pas obligatoire, ainsi dans le cas d'une serie d'affectations, on preferera aligner les operateurs. ex: $query = "SELECT count(msg_id) FROM messages WHERE auteur_usrId='$usrId'"; $result= @mysql_query($query, $link); La declaration de boucle 'for' est une autre exception (voir plus haut): for ($i=1 ; $i<$numArg ; $i+=2) { $result.= func_get_arg($i) .'='. func_get_arg($i+1) .'&'; } > Lors de l'utilisation d'operateurs unaires (ainsi que lors de 'cast'), on n'inserera pas d'espace: $m= -$n; > Tous les operateurs binaires et ternaires sont separes des arguments par un espace de part et d'autre. Lorsqu'il y a imbrication, on alternera : $count= $result ? @mysql_result($result,0,0) : 0 ; if ( $value ) { return ( (int)$word | (int)$flag ); } else { return ( ((int)$word|(int)$flag) ^ (int)$flag ); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ * Recommendations : > Les tests implicites d'égalité à zero sont a eviter if (FAIL != f()) /* ok */ if (f()) /* pas ok */ On preferera les tests d'(in)égalité par rapport à FALSE/0 plutot que par rapport à TRUE. if (FALSE != func()) /* ok */ if (TRUE == func()) /* pas ok */ > On n'oubliera pas que PHP fournit des outils et structures de controles sur les tableaux: foreach () in_array() array_sum() etc... cf. http://fr.php.net/manual/fr/ref.array.php > Il est preferable d'eviter les affectations imbriquees lorsque la lisibilité s'en ressent $d= ($a= $b + $c) + $r; /* pas ok */ > Pour recuperer des variables, on utilisera les tableaux superglobaux introduites en PHP4. $HTTP_POST_VARS et $HTTP_GET_VARS sont a oublier au profits respectifs de $_POST et $_GET > L'utilisation de "Gotcha Keywords" dans les commentaires est encouragée. Quelques mots cles: :TODO: sujet indique les developpements/ameliorations qui seraient bienvenues :BUG: [bugid] sujet indique un bug (qui l'eut cru ?) :INFO: sujet informations et/ou lien sur une subtilite independante de PHP; a utiliser pour expliquer certaines requetes SQL :TRICKY: sujet indique la presence d'une subtilite non triviale (subtiiil) :WARNING: sujet indique que le code est a lire avec precaution (plus que pour TRICKY) :KLUDGE: sujet indique un code fonctionnel mais sans elegance (c.a.d. du code ignoble), ayant besoin d'ameliorations exemple : /* :WARNING: KEY_STRENGHT * * the implementation supports dynamic key resizing which prevents statistic * attacks _even in ECB mode_. it will build a minimal key of 256 bits with * no apparent pattern matching. this mode _cannot_ be used in some countries * that's why it is disabled by default. to enable dynamic key resizing, turn * KEY_STRENGHT to a value of 0. */ /* :INFO: MySQL 'DATABASE()' function * DATABASE() Returns the current database name, if there is no current * database, DATABASE() returns the empty string. */ > Durant les phases de tests, il est souvent necessaire de passer de gros blocs de code en commentaire; generalement on utilise les commentaires sur plusieurs lignes pour cela. Or il y a un probleme si le bloc commenté contient deja un commentaire sur plusieurs lignes: on ne peut pas 'emboiter' les commentaires multi-lignes. Une solution est alors de placer le bloc dans une structure if (FALSE) if (FALSE) { /* code a ignorer */ } Certains editeurs de code (Emacs par exemple) permettent automatiquement de commenter/decommenter tout un bloc.